using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Drawing.Drawing2D;
using System.Threading;
using WeifenLuo.WinFormsUI.Docking;
using MacomberMapSystem.Common.CIM;
using MacomberMapSystem.Common.Integration;
using MacomberMapSystem.Common.Database;
using MacomberMapSystem.Common.Internals;
using MacomberMapSystem.Common.User_Interfaces.One_Lines;
using MacomberMapSystem.Common.User_Interfaces.ModelSampler;

namespace MacomberMapSystem.Common.User_Interfaces.CIM_Browser
{
    /// <summary>
    /// This class allows the user to browse the CIM topology
    /// </summary>
    public partial class ctlCIMBrowser : UserControl
    {
        #region Variable declarations
        /// <summary>Our context menu strip</summary>
        private ContextMenuStrip cms = new ContextMenuStrip();

        /// <summary>The CIM connector associated with this viewer</summary>
        private MM_Database_Model CIM;

        /// <summary>The collection of elements being displayed on the screen</summary>
        public Dictionary<CIM_RdfID, CIM_Element_FrontEnd> DisplayedElements = new Dictionary<CIM_RdfID, CIM_Element_FrontEnd>();
       
        #endregion

        #region Initialization
        /// <summary>
        /// Initialize a new CIM browser
        /// </summary>
        public ctlCIMBrowser(MM_Database_Model CIM)
        {
            this.CIM = CIM;            
            InitializeComponent();
            elemLocator.AssignConnector(CIM);   
        }

        /// <summary>
        /// Assign a new CIM browser
        /// </summary>
        public ctlCIMBrowser()
        {
            InitializeComponent();
        }
       
        #endregion

        #region User interactions


        /// <summary>
        /// Handle the right-click menu on a CIM element
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CIMElem_MouseClick(object sender, MouseEventArgs e)
        {
            CIM_Element_FrontEnd FrontElement = (sender as CIM_Element_FrontEnd);
            if (e.Button == MouseButtons.Left && (Control.ModifierKeys & Keys.Control) == Keys.Control)
            {
                List<CIM_Element> OutElems = new List<CIM_Element>();
                try { OutElems.AddRange(FrontElement.BaseElement.Elements); }
                catch (Exception) { }
                try { OutElems.AddRange(FrontElement.BaseElement.Nodes); }
                catch (Exception) { }
                foreach (CIM_Element Elem in OutElems)
                    AddElement(Elem, FrontElement);
                this.Refresh();
            }
            else if (e.Button == MouseButtons.Left && (Control.ModifierKeys & Keys.Shift) == Keys.Shift)
            {
                List<CIM_Element> OutElems = new List<CIM_Element>();
                OutElems.AddRange(FrontElement.BaseElement.Parse("cim:Line", null));
                OutElems.AddRange(FrontElement.BaseElement.Parse("cim:VoltageLevel", null));
                OutElems.AddRange(FrontElement.BaseElement.Parse("cim:Substation", null));

                foreach (CIM_Element Elem in OutElems)
                    AddElement(Elem,FrontElement);
                this.Refresh();
            }
            else if (e.Button == MouseButtons.Right)
            {
                cms.Items.Clear();
                WriteMenuElement(FrontElement.BaseElement, cms.Items, cms);
                cms.Show(FrontElement, e.Location);
            }
        }
        
        /// <summary>
        /// Write out a menu element
        /// </summary>
        /// <param name="BaseElement">The base element</param>
        /// <param name="cms">The menu strip</param>
        /// <param name="MenuObject">The receiving menu object</param>
        private void WriteMenuElement(CIM_Element BaseElement, ToolStripItemCollection cms, Object MenuObject)
        {
            (cms.Add(BaseElement.rdfID.ToString()) as ToolStripMenuItem).Enabled = !DisplayedElements.ContainsKey(BaseElement.rdfID);
            (cms.Add(BaseElement.TypeName) as ToolStripMenuItem).Enabled = false;
            (cms.Add(BaseElement.Name) as ToolStripMenuItem).Enabled = false;
            if (BaseElement.Substation != null)
            {
                String LongName = BaseElement.Substation["cim:IdentifiedObject.description"];
                if (String.IsNullOrEmpty(LongName) || LongName == BaseElement.Substation.Name)
                    (cms.Add("  In substation " + BaseElement.Substation.Name) as ToolStripMenuItem).Enabled = false;
                else
                    (cms.Add("  In substation " + LongName + " (" + BaseElement.Substation.Name + ")") as ToolStripMenuItem).Enabled = false;
            }
            if (BaseElement.VoltageLevel != null)
                (cms.Add("  " + BaseElement.VoltageLevel.Name) as ToolStripMenuItem).Enabled = false;

            MM_Substation FoundSub;
            if (CIM.Repository.Db.Substations.TryGetValue(BaseElement.rdfID, out FoundSub))
            {
                (cms.Add("  Lat/Long: " + FoundSub.Latitude.ToString() + " / " + FoundSub.Longitude.ToString()) as ToolStripMenuItem).Enabled = false;
                Clipboard.SetText(FoundSub.Name + "\t" + FoundSub.LongName + "\t" + FoundSub.Latitude.ToString() + "\t" + FoundSub.Longitude.ToString());
            }
            UInt64 FoundTEID;
            if (UInt64.TryParse(BaseElement.TEID, out FoundTEID))            
                (cms.Add("TEID: " + BaseElement.TEID) as ToolStripMenuItem).Enabled = false;
            cms.Add("-");

            if (MenuObject is ContextMenuStrip)
                (MenuObject as ContextMenuStrip).ItemClicked += new ToolStripItemClickedEventHandler(cms_ItemClicked);
            else if (MenuObject is ToolStripDropDownItem)
                (MenuObject as ToolStripDropDownItem).DropDownOpening += NewItem_DropDownOpening;
            
            if (BaseElement.InLinks.Length > 0)
            {
                ToolStripMenuItem NewItem = (ToolStripMenuItem)cms.Add("Incoming (" + BaseElement.InLinks.Length.ToString("#,##0") + ")");
                NewItem.DropDownOpening += new EventHandler(NewItem_DropDownOpening);
                NewItem.Tag = BaseElement.InLinks;
                NewItem.DropDownItems.Add("");
            }

            if (BaseElement.OutLinks.Length > 0)
            {
                ToolStripMenuItem NewItem = (ToolStripMenuItem)cms.Add("Outgoing (" + BaseElement.OutLinks.Length.ToString("#,##0") + ")");
                NewItem.DropDownOpening += new EventHandler(NewItem_DropDownOpening);
                NewItem.Tag = BaseElement.OutLinks;
                NewItem.DropDownItems.Add("");
            }

            {

                Dictionary<String, String> OutAttr = BaseElement.Attributes; //BuildAttributes(BaseElement.XML);
                ToolStripMenuItem NewItem = (ToolStripMenuItem)cms.Add("Attributes (" + OutAttr.Count.ToString("#,##0") + ")");
                NewItem.DropDownOpening += new EventHandler(NewItem_DropDownOpening);
                NewItem.Tag = OutAttr;
                NewItem.DropDownItems.Add("");
            }

            List<CIM_Element> OutElems = new List<CIM_Element>();
            try { OutElems.AddRange(BaseElement.Elements); }
            catch (Exception) { }
            try { OutElems.AddRange(BaseElement.Nodes); }
            catch (Exception) { }
            if (OutElems.Count > 0)
            {
                cms.Add("-");
                ToolStripMenuItem NewItem = (ToolStripMenuItem)cms.Add("Elements (" + OutElems.Count.ToString("#,##0") + ")");
                NewItem.DropDownOpening += new EventHandler(NewItem_DropDownOpening);
                NewItem.Tag = OutElems.ToArray();
                NewItem.DropDownItems.Add("");
            }
            CIM_Element[] SPSs = BaseElement.SPSorRAPs;
            if (SPSs.Length > 0)
            {
                cms.Add("-");
                ToolStripMenuItem NewItem = (ToolStripMenuItem)cms.Add("SPS/RAPs (" + SPSs.Length.ToString("#,##0") + ")");
                NewItem.DropDownOpening += new EventHandler(NewItem_DropDownOpening);
                NewItem.Tag = SPSs;
                NewItem.DropDownItems.Add("");
            }


            //Check to see if we have contingencies associated                        
                List<CIM_Trace> Traces = new List<CIM_Trace>();
                foreach (CIM_Trace Trace in CIM.Traces.Values)
                    if (Array.IndexOf(Trace.rdfIDs, BaseElement.rdfID) != -1)
                        Traces.Add(Trace);
            if (Traces.Count > 0)
            {
                cms.Add("-");
                ToolStripMenuItem NewItem = (ToolStripMenuItem)cms.Add("Traces (" + Traces.Count.ToString("#,##0") + ")");
                NewItem.DropDownOpening += new EventHandler(NewItem_DropDownOpening);
                NewItem.Tag = Traces.ToArray() ;
                NewItem.DropDownItems.Add("");
            }

            //If we're a trace, report on the connected traces
            if (BaseElement is CIM_Trace)
            {
                cms.Add("-");
                CIM_Trace[] ConnectedTraces = (BaseElement as CIM_Trace).ConnectedTraces;
                ToolStripMenuItem NewItem = (ToolStripMenuItem)cms.Add("Connected Traces (" + ConnectedTraces.Length.ToString("#,##0") + ")");
                NewItem.DropDownOpening += new EventHandler(NewItem_DropDownOpening);
                NewItem.Tag = ConnectedTraces;
                NewItem.DropDownItems.Add("");
            }
            //Add ownership/operatorship informaiton
            Int64 TryParse;
            if (BaseElement.Owner == null && BaseElement.Operator == null)
            { }
            else if (BaseElement.Owner.Equals(BaseElement.Operator))
            {
                cms.Add("-");
                cms.Add("Owner/Operator: " + MM_Type_Converter.TitleCase(BaseElement.Owner.Name)).Enabled = false;
                if (Int64.TryParse(BaseElement.Owner["PrimaryPhone"], out TryParse))
                    cms.Add("  Phone: " + TryParse.ToString("(000) 000-0000")).Enabled = false;
                else
                    cms.Add("  Phone: " + BaseElement.Owner["PrimaryPhone"]).Enabled = false;
                if (Int64.TryParse(BaseElement.Owner["DUNS"], out TryParse))
                    cms.Add("  DUNS: " + TryParse.ToString("#,##0")).Enabled = false;
                else
                    cms.Add("  DUNS: " + BaseElement.Owner["DUNS"]).Enabled = false;
            }
            else
            {
                cms.Add("-");
                Dictionary<CIM_Element, float> Ownership = BaseElement.ResourceOwners;
                if (Ownership != null)                
                {
                    if (Ownership.Count == 1)
                    {
                        cms.Add("Owner: " + MM_Type_Converter.TitleCase(BaseElement.Owner.Name)).Enabled = false;
                        if (Int64.TryParse(BaseElement.Owner["PrimaryPhone"], out TryParse))
                            cms.Add("  Phone: " + TryParse.ToString("(000) 000-0000")).Enabled = false;
                        else
                            cms.Add("  Phone: " + BaseElement.Owner["PrimaryPhone"]).Enabled = false;
                        if (Int64.TryParse(BaseElement.Owner["DUNS"], out TryParse))
                            cms.Add("  DUNS: " + TryParse.ToString("#,##0")).Enabled = false;
                        else
                            cms.Add("  DUNS: " + BaseElement.Owner["DUNS"]).Enabled = false;
                    }
                    else if (Ownership.Count > 1)
                    {
                        cms.Add("Owners (" + Ownership.Count.ToString() + "): ").Enabled = false;
                        foreach (KeyValuePair<CIM_Element, float> kvp in Ownership)
                        {
                            cms.Add("  " + kvp.Value.ToString("0.0") + "% " + kvp.Key.Name).Enabled = false;
                            if (Int64.TryParse(kvp.Key["PrimaryPhone"], out TryParse))
                                cms.Add("       Phone: " + TryParse.ToString("(000) 000-0000")).Enabled = false;
                            else
                                cms.Add("       Phone: " + kvp.Key["PrimaryPhone"]).Enabled = false;
                            if (Int64.TryParse(kvp.Key["DUNS"], out TryParse))
                                cms.Add("       DUNS: " + TryParse.ToString("#,##0")).Enabled = false;
                            else
                                cms.Add("       DUNS: " + kvp.Key["DUNS"]).Enabled = false;
                        }
                    }
                }
                cms.Add("");
                Dictionary<CIM_Element, float> Operatorship = BaseElement.ResourceOperators;
                if (Operatorship != null)
                {
                    if (Operatorship.Count == 1)
                    {
                        cms.Add("Operator: " + MM_Type_Converter.TitleCase(BaseElement.Operator.Name)).Enabled = false;
                        if (Int64.TryParse(BaseElement.Operator["PrimaryPhone"], out TryParse))
                            cms.Add("  Phone: " + TryParse.ToString("(000) 000-0000")).Enabled = false;
                        else
                            cms.Add("  Phone: " + BaseElement.Operator["PrimaryPhone"]).Enabled = false;
                        if (Int64.TryParse(BaseElement.Operator["DUNS"], out TryParse))
                            cms.Add("  DUNS: " + TryParse.ToString("#,##0")).Enabled = false;
                        else
                            cms.Add("  DUNS: " + BaseElement.Operator["DUNS"]).Enabled = false;
                    }
                    else if (Operatorship.Count > 1)
                    {
                        cms.Add("Operators (" + Operatorship.Count.ToString() + "): ").Enabled = false;
                        foreach (KeyValuePair<CIM_Element, float> kvp in Operatorship)
                        {
                            cms.Add("  " + kvp.Value.ToString("0.0") + "% " + kvp.Key.Name).Enabled = false;
                            if (Int64.TryParse(kvp.Key["PrimaryPhone"], out TryParse))
                                cms.Add("       Phone: " + TryParse.ToString("(000) 000-0000")).Enabled = false;
                            else
                                cms.Add("       Phone: " + kvp.Key["PrimaryPhone"]).Enabled = false;
                            if (Int64.TryParse(kvp.Key["DUNS"], out TryParse))
                                cms.Add("       DUNS: " + TryParse.ToString("#,##0")).Enabled = false;
                            else
                                cms.Add("       DUNS: " + kvp.Key["DUNS"]).Enabled = false;
                        }
                    }
                }                                           
            }

            cms.Add("-");
            ToolStripMenuItem NewItem2 =             cms.Add("&Export model") as ToolStripMenuItem;
            NewItem2.Click += new EventHandler(mnuExportModel_Click);
            NewItem2.Tag = new KeyValuePair<CIM_Element, CIM_RdfID[]>(BaseElement, new List<CIM_RdfID>(DisplayedElements.Keys).ToArray());

            ToolStripMenuItem NewItem3 = cms.Add("&Copy XML to clipboard") as ToolStripMenuItem;
            NewItem3.Click += delegate(Object sender, EventArgs e) { Clipboard.SetText((sender as ToolStripMenuItem).Tag.ToString()); };
            NewItem3.Tag = BaseElement.XML;

            MenuObject.GetType().GetProperty("Tag").SetValue(MenuObject, BaseElement, null);            
        }

        /// <summary>
        /// Handle a model export
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mnuExportModel_Click(object sender, EventArgs e)
        {
            KeyValuePair<CIM_Element, CIM_RdfID[]> kvp = (KeyValuePair<CIM_Element, CIM_RdfID[]>)(sender as ToolStripMenuItem).Tag;
            List<CIM_Element> Elements = new List<CIM_Element>();
            foreach (CIM_RdfID rdfID in kvp.Value)
                Elements.Add(kvp.Key.CIM.FromRdfID(rdfID));
            frmModelSampler.CreateSample(kvp.Key, Elements.ToArray());
        }

        /// <summary>
        /// Handle a new dropdown item opening
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void NewItem_DropDownOpening(object sender, EventArgs e)
        {
            ToolStripDropDownItem DropDownMenu = (ToolStripDropDownItem)sender;
            DropDownMenu.DropDownItems.Clear();
            DropDownMenu.DropDownItemClicked += new ToolStripItemClickedEventHandler(cms_ItemClicked);
            if (DropDownMenu.Tag is CIM_Link[])
            {
                AddDropDownItems(DropDownMenu.Tag as CIM_Link[], DropDownMenu);
                DropDownMenu.DropDownItems.Add("-");
                DropDownMenu.DropDownItems.Add("Add all");
            }
            else if (DropDownMenu.Tag is CIM_Element[])
            {
                AddDropDownItems(DropDownMenu.Tag as CIM_Element[], DropDownMenu);
                DropDownMenu.DropDownItems.Add("-");
                DropDownMenu.DropDownItems.Add("Add all");
            }
            else if (DropDownMenu.Tag is CIM_Link)            
                WriteMenuElement((DropDownMenu.Tag as CIM_Link).Element, DropDownMenu.DropDownItems, DropDownMenu);             
            else if (DropDownMenu.Tag is CIM_Element)
                WriteMenuElement((DropDownMenu.Tag as CIM_Element), DropDownMenu.DropDownItems,DropDownMenu);
            else if (DropDownMenu.Tag is Dictionary<String, String>)
                AddDropDownItems(DropDownMenu.Tag as Dictionary<String, String>, DropDownMenu);
        }

        private Dictionary<String, String> BuildAttributes(String Xml)
        {
            Dictionary<String, String> OutDic = new Dictionary<string, string>(10);
            if (Xml.Length == 0)
                return OutDic;
            int CurPos = 1;
            while ((CurPos = Xml.IndexOf('<', CurPos) + 1) != 0)
            {
                String AttrName = Xml.Substring(CurPos, Xml.IndexOf('>', CurPos) - CurPos);
                if (AttrName.Contains(" "))
                {
                    String Val = AttrName.Substring(AttrName.IndexOf('=') + 2);
                    Val = Val.Substring(0, Val.Length - 2);
                    AttrName = AttrName.Substring(0, AttrName.IndexOf(' '));
                    if (!OutDic.ContainsKey(AttrName))
                        OutDic.Add(AttrName, Val);
                    else
                        for (int a = 0; a < 100; a++)
                            if (!OutDic.ContainsKey(AttrName + "(" + a.ToString() + ")"))
                            {
                                OutDic.Add(AttrName + "(" + a.ToString() + ")", Val);
                                break;
                            }
                }
                else if (AttrName[0] != '/')
                {
                    String Val = Xml.Substring(Xml.IndexOf('>', CurPos) + 1);
                    Val = Val.Substring(0, Val.IndexOf('<'));
                    OutDic.Add(AttrName, Val);
                }
            }
            return OutDic;
        }


        /// <summary>
        /// Add all appropriate drop-down menu elements
        /// </summary>
        /// <param name="Elements">The collection of elements that fit our parameters</param>
        /// <param name="DropDownMenu">The drop down menu to receive the new lines</param>        
        private void AddDropDownItems(CIM_Link[] Elements, ToolStripDropDownItem DropDownMenu)
        {

            //Based on our depth, allocate results accordingly.                                                           
            int CurDepth = GetDepth(DropDownMenu);
            SortedDictionary<String, List<CIM_Link>> Elems = new SortedDictionary<string, List<CIM_Link>>();

            //Go through all elements, and add the appropriate ones into our dictionary
            foreach (CIM_Link ElemLink in Elements)
            {
                CIM_Element Elem = ElemLink.Element;
                String[] splType = Elem.TypeName.Split('.', ':');
                if (CurDepth > splType.GetUpperBound(0))
                {
                    ToolStripMenuItem NewItem = (ToolStripMenuItem)DropDownMenu.DropDownItems.Add(Elem.rdfID.ToString() + " (" + Elem.Name + ")");
                    if (DisplayedElements.ContainsKey(Elem.rdfID))
                        NewItem.Enabled = false;
                    NewItem.Tag = Elem;
                    NewItem.DropDownItems.Add("");
                    NewItem.DropDownOpening += new EventHandler(NewItem_DropDownOpening);
                    NewItem.DropDownItemClicked += new ToolStripItemClickedEventHandler(cms_ItemClicked);
                }
                else if (CurDepth == 1)
                {
                    if (!Elems.ContainsKey(splType[0] + ":" + splType[1]))
                        Elems.Add(splType[0] + ":" + splType[1], new List<CIM_Link>(10));
                    Elems[splType[0] + ":" + splType[1]].Add(ElemLink);
                }
                else
                {
                    if (!Elems.ContainsKey(splType[CurDepth]))
                        Elems.Add(splType[CurDepth], new List<CIM_Link>(10));
                    Elems[splType[CurDepth]].Add(ElemLink);
                }
            }

            foreach (KeyValuePair<String, List<CIM_Link>> Elem in Elems)
            {
                ToolStripMenuItem NewItem = (ToolStripMenuItem)DropDownMenu.DropDownItems.Add(Elem.Key + (Elem.Value.Count > 0 ? " (" + Elem.Value.Count.ToString("#,##0") + ")" : ""));
                NewItem.Tag = Elem.Value.ToArray();
                NewItem.DropDownItems.Add("");
                NewItem.DropDownOpening += new EventHandler(NewItem_DropDownOpening);
                NewItem.DropDownItemClicked += new ToolStripItemClickedEventHandler(cms_ItemClicked);
            }
        }

        /// <summary>
        /// Add all appropriate drop-down menu elements
        /// </summary>
        /// <param name="Elements">The collection of elements that fit our parameters</param>
        /// <param name="DropDownMenu">The drop down menu to receive the new lines</param>        
        private void AddDropDownItems(CIM_Element[] Elements, ToolStripDropDownItem DropDownMenu)
        {

            //Based on our depth, allocate results accordingly.                                                           
            int CurDepth = GetDepth(DropDownMenu);
            SortedDictionary<String, List<CIM_Element>> Elems = new SortedDictionary<string, List<CIM_Element>>();

            //Go through all elements, and add the appropriate ones into our dictionary
            foreach (CIM_Element Elem in Elements)
            {
                String[] splType = Elem.TypeName.Split('.', ':');
                if (CurDepth > splType.GetUpperBound(0))
                {
                    ToolStripMenuItem NewItem = (ToolStripMenuItem)DropDownMenu.DropDownItems.Add(Elem.rdfID.ToString() + " (" + Elem.Name + ")");
                    if (DisplayedElements.ContainsKey(Elem.rdfID))
                        NewItem.Enabled = false;
                    NewItem.Tag = Elem;
                    NewItem.DropDownItems.Add("");
                    NewItem.DropDownOpening += new EventHandler(NewItem_DropDownOpening);
                    NewItem.DropDownItemClicked += new ToolStripItemClickedEventHandler(cms_ItemClicked);
                }
                else if (CurDepth == 1)
                {
                    if (!Elems.ContainsKey(splType[0] + ":" + splType[1]))
                        Elems.Add(splType[0] + ":" + splType[1], new List<CIM_Element>(10));
                    Elems[splType[0] + ":" + splType[1]].Add(Elem);
                }
                else
                {
                    if (!Elems.ContainsKey(splType[CurDepth]))
                        Elems.Add(splType[CurDepth], new List<CIM_Element>(10));
                    Elems[splType[CurDepth]].Add(Elem);
                }
            }

            foreach (KeyValuePair<String, List<CIM_Element>> Elem in Elems)
            {
                ToolStripMenuItem NewItem = (ToolStripMenuItem)DropDownMenu.DropDownItems.Add(Elem.Key + (Elem.Value.Count > 0 ? " (" + Elem.Value.Count.ToString("#,##0") + ")" : ""));
                NewItem.Tag = Elem.Value.ToArray();
                NewItem.DropDownItems.Add("");
                NewItem.DropDownOpening += new EventHandler(NewItem_DropDownOpening);
                NewItem.DropDownItemClicked += new ToolStripItemClickedEventHandler(cms_ItemClicked);
            }
        }

        /// <summary>
        /// Add all appropriate drop-down menu elements
        /// </summary>
        /// <param name="Elements">The collection of elements that fit our parameters</param>
        /// <param name="DropDownMenu">The drop down menu to receive the new lines</param>        
        private void AddDropDownItems(Dictionary<String, String> Elements, ToolStripDropDownItem DropDownMenu)
        {

            //Based on our depth, allocate results accordingly.
            int CurDepth = GetDepth(DropDownMenu);
            SortedDictionary<String, Dictionary<String, String>> Elems = new SortedDictionary<string, Dictionary<String, String>>();

            //Go through all elements, and add the appropriate ones into our dictionary
            foreach (KeyValuePair<String, String> Elem in Elements)
            {
                String[] splType = Elem.Key.Split('.', ':');
                if (CurDepth > splType.GetUpperBound(0))
                { }
                else if (CurDepth == splType.GetUpperBound(0))
                {
                    ToolStripMenuItem NewItem = (ToolStripMenuItem)DropDownMenu.DropDownItems.Add(splType[CurDepth] + " = " + Elem.Value.Replace("&","&&"));
                    NewItem.DropDownItemClicked += new ToolStripItemClickedEventHandler(cms_ItemClicked);
                    if (Elem.Value.Length > 0 && Elem.Value[0] == '#')
                    {
                        CIM_Element ElemToLink = CIM.FromRdfID(Elem.Value.Substring(1));
                        NewItem.Tag = ElemToLink;
                        NewItem.Enabled = !DisplayedElements.ContainsKey(ElemToLink.rdfID);
                    }
                    else
                        NewItem.Enabled = false;
                    NewItem.Tag = Elem;
                }
                else if (CurDepth == 1)
                {
                    if (!Elems.ContainsKey(splType[0] + ":" + splType[1]))
                        Elems.Add(splType[0] + ":" + splType[1], new Dictionary<String, String>(10));
                    Elems[splType[0] + ":" + splType[1]].Add(Elem.Key, Elem.Value);
                }
                else
                {
                    if (!Elems.ContainsKey(splType[CurDepth]))
                        Elems.Add(splType[CurDepth], new Dictionary<String, String>(10));
                    Elems[splType[CurDepth]].Add(Elem.Key, Elem.Value);
                }
            }

            foreach (KeyValuePair<String, Dictionary<String, String>> Elem in Elems)
            {
                ToolStripMenuItem NewItem = (ToolStripMenuItem)DropDownMenu.DropDownItems.Add(Elem.Key + (Elem.Value.Count > 0 ? " (" + Elem.Value.Count.ToString("#,##0") + ")" : ""));
                NewItem.Tag = Elem.Value;
                NewItem.DropDownItems.Add("");
                NewItem.DropDownOpening += new EventHandler(NewItem_DropDownOpening);
                NewItem.DropDownItemClicked += new ToolStripItemClickedEventHandler(cms_ItemClicked);
            }

        }

        /// <summary>
        /// Determine the depth of a menu item
        /// </summary>
        /// <param name="DropDownMenu"></param>
        /// <returns></returns>
        private int GetDepth(ToolStripItem DropDownMenu)
        {
            int Depth = 0;
            do
            {
                Depth++;
                DropDownMenu = DropDownMenu.OwnerItem;
                if (DropDownMenu == null || DropDownMenu.Tag == null || DropDownMenu.Tag is CIM_Element || DropDownMenu.Tag is CIM_Link)
                    return Depth;
            } while (true);            
        }

        /// <summary>
        /// Handle a menu item click
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cms_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            //Determine our highest-parent menu
            ToolStripItem DropDownMenu = (sender as ToolStripItem);
            if (DropDownMenu == null)
                return;
            else
                while (DropDownMenu.OwnerItem != null)
                    DropDownMenu = DropDownMenu.OwnerItem;

            //Determine the clicked CIM element
            CIM_Element ParentElem = cms.Tag as CIM_Element;
            CIM_Element_FrontEnd FoundElem = null;
            if (ParentElem != null)
                FoundElem = DisplayedElements[ParentElem.rdfID];


            if (e.ClickedItem.Text == "Add all" && e.ClickedItem.OwnerItem.Tag is CIM_Link[])
                foreach (CIM_Link ElemToAdd in (e.ClickedItem.OwnerItem.Tag as CIM_Link[]))
                    AddElement(ElemToAdd.Element, FoundElem);
            else if (e.ClickedItem.Text == "Add all" && e.ClickedItem.OwnerItem.Tag is CIM_Element[])
                foreach (CIM_Element ElemToAdd in (e.ClickedItem.OwnerItem.Tag as CIM_Element[]))
                    AddElement(ElemToAdd, FoundElem);
            else if (e.ClickedItem.Tag is CIM_Element)
                AddElement(e.ClickedItem.Tag as CIM_Element, FoundElem);
            else if (e.ClickedItem.Tag is CIM_Link)
                AddElement((e.ClickedItem.Tag as CIM_Link).Element, FoundElem);
            else if (e.ClickedItem.Tag is KeyValuePair<String, String> && ((KeyValuePair<String, String>)e.ClickedItem.Tag).Value.StartsWith("#"))
                AddElement(CIM.FromRdfID((((KeyValuePair<String, String>)e.ClickedItem.Tag)).Value.Substring(1)), FoundElem);

            //Clear the screen
            cms.Hide();
            splMain.Panel2.Invalidate();
        }

        /// <summary>
        /// Locate or create a new CIM element front end
        /// </summary>
        /// <param name="Link"></param>
        /// <returns></returns>
        private CIM_Element_FrontEnd LocateElementFrontEnd(CIM_Element Link)
        {
            if (Link == null)
                return null;
            else if (DisplayedElements.ContainsKey(Link.rdfID))
                return DisplayedElements[Link.rdfID];
            else
            {
                CIM_Element_FrontEnd NewOut = new CIM_Element_FrontEnd(Link);
                NewOut.RecomputeText(btnRdfID.Checked, btnType.Checked, btnName.Checked, btnTerminals.Checked);
                NewOut.MouseDown += CIMElem_MouseDown;
                NewOut.MouseMove += CIMElem_MouseMove;
                NewOut.MouseUp += CIMElem_MouseUp;
                NewOut.MouseClick += CIMElem_MouseClick;
                DisplayedElements.Add(Link.rdfID, NewOut);
                splMain.Panel2.Controls.Add(NewOut);
                return NewOut;
            }
        }

        /// <summary>
        /// Add an element to the display
        /// </summary>
        /// <param name="ElementToAdd"></param>
        /// <param name="ParentElement"></param>
        public void AddElement(CIM_Element ElementToAdd, CIM_Element_FrontEnd ParentElement)
        {
            //Locate our element
            CIM_Element_FrontEnd Elem = LocateElementFrontEnd(ElementToAdd);
            if (Elem.BaseElement.TypeName == "cim:Terminal")
                Elem.Visible = btnTerminals.Checked;

            //Check to see if we have any of this element's outgoing links, and if so, add them. Also check to see if a terminal is in between the two
            foreach (CIM_Link ElemToCheckLink in ElementToAdd.OutLinks)
                if (DisplayedElements.ContainsKey(ElemToCheckLink.rdfID) && !Elem.OutgoingElements.Contains(DisplayedElements[ElemToCheckLink.rdfID]))
                    Elem.OutgoingElements.Add(DisplayedElements[ElemToCheckLink.rdfID]);

            //Now check the element's incoming links.
            foreach (CIM_Link ElemToCheckLink in ElementToAdd.InLinks)
                if (DisplayedElements.ContainsKey(ElemToCheckLink.rdfID) && !DisplayedElements[ElemToCheckLink.rdfID].OutgoingElements.Contains(Elem))
                    DisplayedElements[ElemToCheckLink.rdfID].OutgoingElements.Add(Elem);

            //Now, check for terminals
            List<CIM_Element> TerminalsToAdd = new List<CIM_Element>();
           /* foreach (CIM_RdfID ElemToCheck in DisplayedElements.Keys)
                if (ElemToCheck != ElementToAdd.rdfID)
                    foreach (CIM_Link Term1 in ElementToAdd.Links)
                        foreach (CIM_Link Term2 in CIM.FromRdfID(ElemToCheck).Links)
                        //foreach (CIM_Link Term2 in ElemToCheck.Links)
                            if (Term1.TypeName == "cim:Terminal" && Term1.rdfID != Term2.rdfID && !DisplayedElements.ContainsKey(Term1.rdfID))
                                TerminalsToAdd.Add(Term1.Element);
            */
            foreach (CIM_Element Terminal in TerminalsToAdd)
                AddElement(Terminal, Elem);

            //Position our element if needed
            if (ParentElement != null && Elem.Location.IsEmpty)
            {
                Point CursorPos = new Point(ParentElement.Left + (ParentElement.Width / 2), ParentElement.Top + (ParentElement.Height / 2));                
                while (splMain.Panel2.GetChildAtPoint(CursorPos) != null)
                    CursorPos.X += 20;
                Elem.Left = CursorPos.X;
                Elem.Top = CursorPos.Y - (Elem.Height/2);
            }
        }

        /// <summary>
        /// Handle the mouse up on a CIM element
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CIMElem_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
                (sender as Control).Tag = null;
        }

        /// <summary>
        /// Handle the mouse down on a CIM element
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CIMElem_MouseDown(object sender, MouseEventArgs e)
        {

            if (e.Button == MouseButtons.Left)
            {
                CIM_Element_FrontEnd Sender = (sender as CIM_Element_FrontEnd);
                Sender.Tag = new Point(Cursor.Position.X - Sender.Left, Cursor.Position.Y - Sender.Top);
                Sender.BringToFront();
            }
        }

        /// <summary>
        /// Handle the mouse down on a CIM element
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CIMElem_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                CIM_Element_FrontEnd Sender = (sender as CIM_Element_FrontEnd);
                if (Sender.Tag == null)
                {
                    Sender.Tag = new Point(Cursor.Position.X - Sender.Left, Cursor.Position.Y - Sender.Top);
                    Sender.BringToFront();
                }
                Point StartPosition = (Point)Sender.Tag;
                Sender.Location = new Point(Cursor.Position.X - StartPosition.X, Cursor.Position.Y - StartPosition.Y);
                splMain.Panel2.Invalidate();
            }
        }



        #endregion



        /// <summary>
        /// Go through all CIM elements, and draw arrows to their outgoing links
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void splMain_Panel2_Paint(object sender, PaintEventArgs e)
        {
            //Update our status when it's needed
            if (CIM != null && CIM.State != MM_Database_Model.enumModelState.Completed)
                e.Graphics.DrawString("Model: " + CIM.State.ToString().Replace('_',' ') + " (" + CIM.StepProgress.ToString("0%") + ")", this.Font, Brushes.Black, 5,5);


            //First, draw all visible links.
            foreach (CIM_Element_FrontEnd FrontEndElement in DisplayedElements.Values)
                if (FrontEndElement.Visible)
                    foreach (CIM_Element_FrontEnd TargetElement in FrontEndElement.OutgoingElements)
                        DrawArrow(FrontEndElement.Bounds, TargetElement.Bounds, e.Graphics, true);


            //Now, draw all the invisible links around the arrow.
            foreach (CIM_Element_FrontEnd FrontEndElement in DisplayedElements.Values)
                if (!FrontEndElement.Visible)
                    for (int a = 0; a < FrontEndElement.OutgoingElements.Count; a++)
                        for (int b = a + 1; b < FrontEndElement.OutgoingElements.Count; b++)
                            DrawArrow(FrontEndElement.OutgoingElements[a].Bounds, FrontEndElement.OutgoingElements[b].Bounds, e.Graphics, false);
        }

        /// <summary>
        /// Draw an arrow between two points
        /// </summary>
        /// <param name="SourceRect">The starting rectangle</param>
        /// <param name="TargetRect">The target rectangle</param>
        /// <param name="g">The graphics connection</param>
        /// <param name="ArrowEnd">Whether an arrow should appear at the end</param>
        private void DrawArrow(Rectangle SourceRect, Rectangle TargetRect, Graphics g, bool ArrowEnd)
        {
            Point StartPoint = new Point(SourceRect.X + (SourceRect.Width / 2), SourceRect.Y + (SourceRect.Height / 2));
            Point EndPoint = new Point(TargetRect.X + (TargetRect.Width / 2), TargetRect.Y + (TargetRect.Height / 2));

            if (TargetRect.Top > SourceRect.Bottom)
                EndPoint.Y = TargetRect.Top;
            else if (SourceRect.Top > TargetRect.Bottom)
                EndPoint.Y = TargetRect.Bottom;
            if (TargetRect.Left > SourceRect.Right)
                EndPoint.X = TargetRect.Left;
            else if (SourceRect.Left > TargetRect.Right)
                EndPoint.X = TargetRect.Right;

            if (SourceRect.Top > TargetRect.Bottom)
                StartPoint.Y = SourceRect.Top;
            else if (TargetRect.Top > SourceRect.Bottom)
                StartPoint.Y = SourceRect.Bottom;
            if (SourceRect.Left > TargetRect.Right)
                StartPoint.X = SourceRect.Left;
            else if (TargetRect.Left > SourceRect.Right)
                StartPoint.X = SourceRect.Right;


            using (Pen OutPen = new Pen(Color.Black, 3f))
            {
                OutPen.StartCap = (ArrowEnd ? LineCap.Round : LineCap.ArrowAnchor);
                OutPen.EndCap = LineCap.ArrowAnchor;
                OutPen.LineJoin = LineJoin.Bevel;
                OutPen.Color = ArrowEnd ? Color.Black : Color.Green;
                g.DrawLine(OutPen, StartPoint, EndPoint);
            }
        }

        /// <summary>
        /// Handle the user's changing a CIM parameter.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UpdateVisibility(object sender, EventArgs e)
        {
            ToolStripButton Sender = (ToolStripButton)sender;
            Sender.Checked ^= true;
            if (Sender != btnTerminals)
                foreach (CIM_Element_FrontEnd Elem in DisplayedElements.Values)
                    Elem.RecomputeText(btnRdfID.Checked, btnType.Checked, btnName.Checked, toolStripButton1.Checked);
            else
                foreach (CIM_Element_FrontEnd Elem in DisplayedElements.Values)
                    if (Elem.BaseElement.TypeName == "cim:Terminal")
                        Elem.Visible = Sender.Checked;
            this.splMain.Panel2.Refresh();
        }

        /// <summary>
        /// Write an SVG line between two points
        /// </summary>
        /// <param name="xW">The XML text writer</param>
        /// <param name="StartLine">The start of the line</param>
        /// <param name="EndLine">The end of the line</param>
        /// <param name="DualArrows">Whether dual arrows should be used</param>
        private void WriteLine(XmlTextWriter xW, CIM_Element_FrontEnd StartLine, CIM_Element_FrontEnd EndLine, bool DualArrows)
        {
            //Determine our points
            Rectangle SourceRect = StartLine.Bounds;
            Rectangle TargetRect = EndLine.Bounds;
            Point StartPoint = new Point(SourceRect.X + (SourceRect.Width / 2), SourceRect.Y + (SourceRect.Height / 2));
            Point EndPoint = new Point(TargetRect.X + (TargetRect.Width / 2), TargetRect.Y + (TargetRect.Height / 2));

            if (TargetRect.Top > SourceRect.Bottom)
                EndPoint.Y = TargetRect.Top;
            else if (SourceRect.Top > TargetRect.Bottom)
                EndPoint.Y = TargetRect.Bottom;
            if (TargetRect.Left > SourceRect.Right)
                EndPoint.X = TargetRect.Left;
            else if (SourceRect.Left > TargetRect.Right)
                EndPoint.X = TargetRect.Right;

            if (SourceRect.Top > TargetRect.Bottom)
                StartPoint.Y = SourceRect.Top;
            else if (TargetRect.Top > SourceRect.Bottom)
                StartPoint.Y = SourceRect.Bottom;
            if (SourceRect.Left > TargetRect.Right)
                StartPoint.X = SourceRect.Left;
            else if (TargetRect.Left > SourceRect.Right)
                StartPoint.X = SourceRect.Right;


            xW.WriteStartElement("line");
            xW.WriteAttributeString("x1", StartPoint.X.ToString());
            xW.WriteAttributeString("y1", StartPoint.Y.ToString());
            xW.WriteAttributeString("x2", EndPoint.X.ToString());
            xW.WriteAttributeString("y2", EndPoint.Y.ToString());
            xW.WriteAttributeString("stroke", "black");
            xW.WriteAttributeString("stroke-width", "3");
            if (DualArrows)
                xW.WriteAttributeString("marker-start", "url(#startArrow)");
            xW.WriteAttributeString("marker-end", "url(#endArrow)");
            xW.WriteEndElement();
        }


        /// <summary>
        /// Export the current workspace as a SVG document
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSave_Click(object sender, EventArgs e)
        {
            //Save an SVG file

            using (SaveFileDialog s = new SaveFileDialog())
            {
                s.Title = "Save SVG of CIM topology";
                s.Filter = "SVG Graphics File (*.svg)|*.svg";
                if (s.ShowDialog() == DialogResult.OK)
                    using (XmlTextWriter xW = new XmlTextWriter(s.FileName, Encoding.UTF8))
                    {
                        xW.Formatting = Formatting.Indented;
                        xW.WriteStartDocument(false);
                        xW.WriteDocType("svg", "-//W3C//DTD SVG 1.1//EN", "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd", null);
                        xW.WriteStartElement("svg", "http://www.w3.org/2000/svg");
                        xW.WriteAttributeString("width", "100%");
                        xW.WriteAttributeString("height", "100%");
                        xW.WriteAttributeString("version", "1.1");

                        xW.WriteStartElement("defs");
                        xW.WriteStartElement("marker");
                        xW.WriteAttributeString("id", "endArrow");
                        xW.WriteAttributeString("viewBox", "0 0 10 10");
                        xW.WriteAttributeString("refX", "1");
                        xW.WriteAttributeString("refY", "5");
                        xW.WriteAttributeString("markerUnits", "strokeWidth");
                        xW.WriteAttributeString("orient", "auto");
                        xW.WriteAttributeString("markerWidth", "5");
                        xW.WriteAttributeString("markerHeight", "4");
                        xW.WriteStartElement("polyline");
                        xW.WriteAttributeString("points", "0,0 10,5 0,10 1,5");
                        xW.WriteAttributeString("fill", "black");
                        xW.WriteEndElement();
                        xW.WriteEndElement();
                        xW.WriteStartElement("marker");
                        xW.WriteAttributeString("id", "startArrow");
                        xW.WriteAttributeString("viewBox", "0 0 10 10");
                        xW.WriteAttributeString("refX", "1");
                        xW.WriteAttributeString("refY", "5");
                        xW.WriteAttributeString("markerUnits", "strokeWidth");
                        xW.WriteAttributeString("orient", "auto");
                        xW.WriteAttributeString("markerWidth", "5");
                        xW.WriteAttributeString("markerHeight", "4");
                        xW.WriteStartElement("polyline");
                        xW.WriteAttributeString("points", "10,0 0,5 10,10 9,5");
                        xW.WriteAttributeString("fill", "black");
                        xW.WriteEndElement();
                        xW.WriteEndElement();
                        xW.WriteEndElement();



                        //First, write out our arrows
                        foreach (CIM_Element_FrontEnd LineToWrite in DisplayedElements.Values)
                            if (LineToWrite.Visible)
                                foreach (CIM_Element_FrontEnd OtherSide in LineToWrite.OutgoingElements)
                                    if (OtherSide.Visible)
                                        WriteLine(xW, LineToWrite, OtherSide, false);

                        //Now, write out our terminals
                        foreach (CIM_Element_FrontEnd LineToWrite in DisplayedElements.Values)
                            if (!LineToWrite.Visible)
                                for (int a = 0; a < LineToWrite.OutgoingElements.Count; a++)
                                    for (int b = a + 1; b < LineToWrite.OutgoingElements.Count; b++)
                                        WriteLine(xW, LineToWrite.OutgoingElements[a], LineToWrite.OutgoingElements[b], true);

                        //Now, write out each element.
                        foreach (CIM_Element_FrontEnd ElemToWrite in DisplayedElements.Values)
                            if (ElemToWrite.Visible)
                            {
                                xW.WriteStartElement("rect");
                                xW.WriteAttributeString("x", ElemToWrite.Left.ToString());
                                xW.WriteAttributeString("y", ElemToWrite.Top.ToString());
                                xW.WriteAttributeString("width", ElemToWrite.Width.ToString());
                                xW.WriteAttributeString("height", ElemToWrite.Height.ToString());
                                xW.WriteAttributeString("fill", ColorTranslator.ToHtml(ElemToWrite.BackColor));
                                xW.WriteEndElement();

                                xW.WriteStartElement("text");
                                xW.WriteAttributeString("x", ElemToWrite.Left.ToString());
                                xW.WriteAttributeString("y", ElemToWrite.Top.ToString());
                                xW.WriteAttributeString("fill", ColorTranslator.ToHtml(ElemToWrite.ForeColor));
                                xW.WriteAttributeString("font-size", ElemToWrite.Font.SizeInPoints.ToString() + "pt");
                                xW.WriteAttributeString("text-anchor", "middle");
                                xW.WriteAttributeString("font-family", ElemToWrite.Font.FontFamily.Name);

                                String[] LinesToWrite = ElemToWrite.Text.Split('\n');
                                for (int a = 0; a < LinesToWrite.Length; a++)
                                {
                                    xW.WriteStartElement("tspan");
                                    xW.WriteAttributeString("x", (ElemToWrite.Left + (ElemToWrite.Width / 2)).ToString());
                                    if (a == 0)
                                        xW.WriteAttributeString("y", (ElemToWrite.Top + ElemToWrite.Font.Height).ToString());
                                    else
                                        xW.WriteAttributeString("dy", ElemToWrite.Font.Height.ToString());
                                    xW.WriteString(LinesToWrite[a].Trim());
                                    xW.WriteEndElement();
                                }
                                xW.WriteEndElement();
                            }
                        xW.WriteEndElement();
                        xW.Flush();
                        xW.Close();

                    }
                MessageBox.Show("SVG export complete", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        /// <summary>
        /// Clear the workspace
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnClear_Click(object sender, EventArgs e)
        {
            lock (this)
            {                
                foreach (CIM_RdfID ElemToDispose in DisplayedElements.Keys)
                    DisplayedElements[ElemToDispose].Dispose();
                DisplayedElements.Clear();
                splMain.Panel2.Controls.Clear();
                this.Invalidate();
                this.Refresh();
            }
        }

        private void btnDefaultCIMBrowser_Click(object sender, EventArgs e)
        {
            if (MM_Database_Model.DefaultTarget != null)
                MM_Database_Model.DefaultTarget.btnTarget.Checked = false;
            else if (MM_Database_Model.DefaultTarget == this)
            {
                MM_Database_Model.DefaultTarget = null;
                btnTarget.Checked = false;
            }
            else
            {
                MM_Database_Model.DefaultTarget = this;
                btnTarget.Checked = true;
            }
        }


        #region Db Interfaces
        /// <summary>
        /// Assign our database connector
        /// </summary>
        /// <param name="Model"></param>
        public void SetConnector(MM_Database_Model Model)
        {
            if (Model != this.CIM)
            {
                this.CIM = Model;
                elemLocator.AssignConnector(Model);
            }
            
        }

        /// <summary>
        /// Clear all elements
        /// </summary>
        public void ClearElements()
        {
            btnClear_Click(btnClear, EventArgs.Empty);
        }
        #endregion


        /// <summary>
        /// Refresh our panel every second when the model's not loaded
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tmrUpdate_Tick(object sender, EventArgs e)
        {
            if (CIM != null)
            if (CIM.State == MM_Database_Model.enumModelState.Completed)
            {
                this.elemLocator.AssignConnector(CIM);
                tmrUpdate.Stop();
            }
            else            
                splMain.Panel2.Refresh();
        }

        /// <summary>
        /// Perform our Tom Sawyer layout of elements
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnAutoLayout_Click(object sender, EventArgs e)
        {
            clsTomSawyer.RunLayout(DisplayedElements.Values, CIM.Repository);

        }


       
    }
}